#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "include/params.h"
#include "include/types.h"
#include "include/operat.h"
#include "include/virtware.h"
#include "include/module.h"
#include "include/vmerr.h"
#include "include/nr_excp.h"
#include "include/libcore.h"

#define PG4K_MASK (~0xFFFU)

status mem_init (struct memory * mem, uint32 size, struct environ * env);
status mem_add_module (struct memory * mem, struct module * mod);
status mem_remove_module (struct memory * mem, modid_t modid);
status mem_rw_phy (struct environ* env, memaddr_t addr, word send, word * recv, enum rwmod rw);
status mem_rw_virt (struct environ* env, memaddr_t addr, word send, word * recv, enum rwmod rw);

status
mem_init (struct memory * mem, uint32 size, struct environ * env) {
    status stat = STAT_SUCCESS;
    bzero (mem, sizeof (struct memory));
    if((mem->data = (int8*) malloc (size & PG4K_MASK)) == NULL) {
        stat |= ERR_NOMEM;
        DBG_LOG (mem_init, "Insufficient memory");
        goto error;
    }
    mem->size = size & PG4K_MASK;
    mem->env = env;
    mem->magic = BISCUIT_MAGIC;
    return stat;
    error:
    return stat;
}

status
mem_add_module (struct memory * mem, struct module * mod) {
    status stat = STAT_SUCCESS;
    count_t i;
    for (i = 0; i < mod->items.item_n; i++) {
        if (mod->items.item_list[i].type == MI_MEM_PHYRW) 
            add_module_item (mem, phyrw, mod->items.item_list[i]);
    }
    for (i = 0; i < mod->items.item_n; i++) {
        if (mod->items.item_list[i].type == MI_MEM_VIRTRW) 
            add_module_item (mem, virtrw, mod->items.item_list[i]);
    }
    if (stat & ERR_NRITEM) {
        fprintf (stderr, "mem_add_module: Unable to add module, MAX_MOD_ITEMS reached.\n");
        DBG_LOG (mem_add_module,"removing mod[%llu]::phyrw", mod->modid);
        remove_module_item (mem, phyrw,mod->modid);
        DBG_LOG (mem_add_module,"mod[%llu]::phyrw removed", mod->modid);
        DBG_LOG (mem_add_module,"removing mod[%llu]::virtrw", mod->modid);
        remove_module_item (mem, virtrw,mod->modid);
        DBG_LOG (mem_add_module,"mod[%llu]::phyrw virtrw", mod->modid);
    }
    return stat;
}


status
mem_remove_module (struct memory * mem, modid_t modid) {
    status stat = STAT_SUCCESS;
    bool mod_exist = false;
    count_t i;
    for (i = 0; i < MAX_MOD_ITEM; i++) {
        if (mem->mods.phyrw_modid[i] == modid){
            mod_exist = true;
            break;
        }
        if (mem->mods.virtrw_modid[i] == modid){
            mod_exist = true;
            break;
        }
    }
    if (mod_exist == false) {
        stat |= ERR_MNOINST;
        goto error;
    }
    remove_module_item (mem, phyrw, modid);
    remove_module_item (mem, virtrw, modid);
    return stat;
    error:
    return stat;
}

status
mem_rw_phy (struct environ* env, memaddr_t addr, word send, word * recv, enum rwmod rw) {
    status stat = STAT_SUCCESS;
    struct req_stat rstat = REQS_INIT;
    struct memory * mem = &env->mem;
    exec_module_func (mem, phyrw, { 
        .env = env,
        .addr = addr,
        .send = send,
        .recv = recv,
        .rw = rw,
        .rstat = &rstat
    });
    return stat;
}

status
mem_rw_virt (struct environ* env, memaddr_t addr, word send, word * recv, enum rwmod rw) {
    status stat = STAT_SUCCESS;
    struct req_stat rstat = REQS_INIT;
    struct memory * mem = &env->mem;
    exec_module_func (mem, virtrw, { 
        .env = env,
        .addr = addr,
        .send = send,
        .recv = recv,
        .rw = rw,
        .rstat = &rstat
    });
    return stat;
}